import { LoginUser } from 'f2e-server3';
import { FetchAPI, PageParams, Pager, User, UserService } from '../../interface'
import { Db, Filter } from 'mongodb'
import { md5 } from '../../utils';

export class UserServiceImpl implements UserService {
    public CONN_NAME = 'rbac_user';
    private db: Db
    private mock_user?: User['username']
    constructor(db: Db, mock_user?: User['username']) {
        this.db = db
        this.mock_user = mock_user
    }
    userCreate: FetchAPI<User[]> = async (users) => {
        const keys: string[] = []
        let error: string | undefined
        const key_set = new Set(users.map(p => {
            if (!p.username || !p.password) {
                error = '用户名不能为空'
            } else {
                keys.push(p.username)
                p.password = md5(p.password)
            }
            return p.username
        }))
        if (error) {
            return {
                success: false,
                error
            }
        }
        if (key_set.size !== keys.length) {
            return {
                success: false,
                error: '用户名重复',
            }
        }
        const conn = this.db.collection<User>(this.CONN_NAME)
        const exist = await conn.find({ username: { $in: keys } }).toArray()
        if (exist.length) {
            return {
                success: false,
                error: '用户名重复',
            }
        }
        await conn.insertMany(users)
        return {
            success: true,
        }
    }
    userDelete: FetchAPI<string[]> = async (usernames) => {
        const conn = this.db.collection<User>(this.CONN_NAME)
        await conn.deleteMany({ username: { $in: usernames } })
        usernames.forEach(username => {
            for (const callback of this.deleteCallbacks) {
                callback(username)
            }
        })
        return {
            success: true,
        }
    }
    userUpdate: FetchAPI<Partial<User> & { username: User['username']; }> = async (user) => {
        const conn = this.db.collection<User>(this.CONN_NAME)
        await conn.updateOne({ username: user.username }, { $set: user })
        return {
            success: true
        }
    }
    userList: FetchAPI<PageParams<Partial<User>>, Pager<User>> = async (body) => {
        const { page = 1, pageSize = 10, params = {} } = body;
        const offset = (page - 1) * pageSize
        const conn = this.db.collection<User>(this.CONN_NAME)
        const { username, nickname, roles = [] } = params
        const filter: Filter<User> = {}
        if (username) {
            filter.username = username
        }
        if (nickname) {
            filter.nickname = { $regex: nickname, $options: 'i' }
        }
        if (roles.length) {
            filter.roles = { $in: roles }
        }
        const list = await conn.find(filter).skip(offset).limit(pageSize).toArray()
        return {
            success: true,
            data: {
                list,
                total: await conn.countDocuments(filter),
                page,
                pageSize,
            }
        }
    }
    getUserByUsername: (username: User['username']) => Promise<User | undefined> = async (username) => {
        const conn = this.db.collection<User>(this.CONN_NAME)
        return await conn.findOne({ username }) || undefined
    }
    async getUser(username: string, password: string): Promise<LoginUser | undefined> {
        const conn = this.db.collection<User>(this.CONN_NAME)
        return await conn.findOne({ username, password: md5(password) }) || undefined
    }
    async getLoginUser(crsf_token?: string): Promise<LoginUser | undefined> {
        if (this.mock_user) {
            const conn = this.db.collection<User>(this.CONN_NAME)
            return await conn.findOne({ username: this.mock_user }) || undefined
        }
        return undefined
    }
    private deleteCallbacks: {(username: string): void}[] = []
    onDeleteUser(callback: (username: string) => void): void {
        this.deleteCallbacks.push(callback);
    }
    
}